Conversation
…tter readability.
… handling numerical edge cases.
This commit introduces a new example script demonstrating the use of anisotropic friction in the IPC Toolkit. The script showcases the setup of a simple collision scenario, building normal and tangential collisions, assigning anisotropic friction coefficients, and computing effective friction for various velocity directions. Additionally, it compares the results with isotropic friction to highlight the differences in behavior.
… of effective friction coefficients and ensure finite results. Update documentation to reflect changes in function name and return values.
…nd improve clarity. Update documentation and tests to reflect changes from `anisotropic_mu_eff_sqrt_mu0_t0_sq_plus_mu1_t1_sq` to `anisotropic_mu_eff_f` and related functions.
…ility by consolidating function calls into single lines. This change enhances clarity without altering the test logic.
… and consistency. Convert markdown cells to code cells in the notebook, enhance symbolic variable definitions, and streamline friction force visualization. Update test cases to ensure consistent function calls and improve readability.
…move backward compatibility section from the tutorial, update execution counts in the notebook, and streamline function definitions in the C++ code. Enhance test cases for consistency and readability.
This commit introduces the Matchstick model for anisotropic friction, enhancing the IPC Toolkit's capabilities. Key changes include: - Addition of the Matchstick model reference in `references.bib`. - Updates to the C++ and Python API documentation to include anisotropic friction helpers and their usage. - Enhancements in the `advanced_friction` tutorial to cover anisotropic friction features and their implementation. - New tests for anisotropic friction transitions and effective coefficient calculations. Documentation now references Erleben et al. (2019) for the model's derivation and implementation details.
…2/ipc-toolkit into anisotropic_friction
…prove code comments for clarity in tangential collision and potential implementations.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #210 +/- ##
==========================================
+ Coverage 95.77% 95.84% +0.07%
==========================================
Files 159 160 +1
Lines 16521 16655 +134
Branches 927 944 +17
==========================================
+ Hits 15823 15963 +140
+ Misses 698 692 -6
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Woooow! This is really awesome. Thanks for the contribution. The matchstick method for anisotropic friction was something I was interested in implementing, so I am super excited you did it already. 😄 I'll review the changes in depth when I have some free time. 🚀 |
There was a problem hiding this comment.
Pull request overview
This PR adds anisotropic friction support to IPC Toolkit’s tangential contact model by introducing per-collision anisotropy parameters, helper math for effective direction-dependent friction coefficients, Python bindings, and expanded docs/tests.
Changes:
- Added anisotropic friction parameters on
TangentialCollision(mu_aniso,mu_s_aniso,mu_k_aniso) and exposed them to Python. - Updated tangential friction potential/force/Jacobian computations to use anisotropically scaled tangential velocity and effective-μ helpers.
- Added anisotropic effective-μ helper functions plus new/updated unit tests and documentation.
Reviewed changes
Copilot reviewed 17 out of 18 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
src/ipc/potentials/tangential_potential.cpp |
Integrates anisotropic scaling/effective-μ into friction potential and force/Jacobian computations. |
src/ipc/friction/smooth_mu.hpp |
Declares new anisotropic effective-μ helpers and derivatives. |
src/ipc/friction/smooth_mu.cpp |
Implements effective-μ evaluation and derivative helpers. |
src/ipc/collisions/tangential/tangential_collision.hpp |
Adds per-collision anisotropy parameters to the tangential collision model. |
python/src/friction/smooth_mu.cpp |
Exposes anisotropic effective-μ helpers to Python. |
python/src/collisions/tangential/tangential_collision.cpp |
Exposes new tangential collision anisotropy fields to Python. |
tests/src/tests/potential/test_friction_potential.cpp |
Updates friction potential FD tests to handle filtered meshes/displacement mapping. |
tests/src/tests/friction/test_smooth_mu.cpp |
Adds unit tests for anisotropic effective-μ helpers and derivatives. |
tests/src/tests/friction/test_force_jacobian.cpp |
Adjusts force/Jacobian tests for mesh sizing and adds coverage for no-μ paths. |
tests/src/tests/friction/test_anisotropic_friction.cpp |
Adds new anisotropic friction tests (helpers + force/Jacobian scenarios). |
tests/src/tests/friction/CMakeLists.txt |
Registers the new anisotropic friction test file. |
docs/source/tutorials/advanced_friction.rst |
Adds an anisotropic friction tutorial section and usage examples. |
docs/source/cpp-api/friction.rst |
Documents anisotropic helper APIs for C++. |
docs/source/python-api/friction.rst |
Documents anisotropic helper APIs for Python. |
docs/source/references.bib |
Adds the matchstick model citation. |
docs/source/developers/tools.rst / docs/source/developers/style_guide.rst |
Clarifies formatting/tooling expectations (clang-format version/style). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Update TangentialPotential::gradient to include contributions from the gradients of effective mu_s and mu_k (direction-dependent friction), obtaining grad_mu_* via anisotropic_mu_eff_f_grad and adding the corresponding drift terms. Add virtual mu_f0_grad_mu_s/mu_k declarations and provide finite-difference fallback implementations in friction_potential.hpp and tangential_adhesion_potential.hpp. Update tests: replace M_PI with igl::PI and add a test that verifies the TangentialPotential gradient and Hessian against finite differences. WARNING: tests fail because Hessian has not been updated to account for anisotropic coefficients
|
Hi Antoine, I’ve put together a document detailing the challenges of integrating anisotropic friction into a fully implicit framework: Anisotropic_IPC_Friction.pdf. The core issue is that anisotropic coefficients create a nonzero curl on the tangent vector space. Because of this, we can't derive an incremental potential that perfectly matches the required forces. While the document proposes an approximate potential, I recommend lagging the anisotropic coefficients—similar to our current approach with normal forces and tangent bases—as a more practical path forward. -Z |
|
Hi Zachary, Thanks for sharing the PDF, it clarified a lot for me, especially the curl issue (I hadn’t considered that case at all) and the lagged anisotropy approach. I’ve been a bit busy with school lately, but I’m excited to get back to this. My current implementation is fairly naive, so I’ll likely rework it to better follow your formulation probably starting with the lagged version since it fits well with the existing codebase. I’ll keep you posted once I have something close to working Best, |
…ian BarrierPotential API Made-with: Cursor
- Add mu_s_effective_lagged/mu_k_effective_lagged and TangentialCollisions reset/update methods; refresh after build and when slip changes. - Use lagged scalars in dissipative potential, force, and Jacobians; remove ad-hoc d(mu)/d(tau) Jacobian terms and align smooth_contact jacobian with force. - Python: expose lagged fields and update/reset on TangentialCollisions. - Tests: BarrierPotential(dhat, kappa) and build API; call update for anisotropic cases; tighten combined FD check; add gradient vs -force test; extend smooth 3D jacobian check with lagged anisotropic FD. - Docs: advanced_friction lagging section and BarrierPotential build snippets. Made-with: Cursor
…2/ipc-toolkit into anisotropic_friction
…ntries, Cohen doi) Made-with: Cursor
- Removed the `anisotropic_mu_eff_f_grad` function from the C++ API and related documentation, as it is no longer needed for the lagged matchstick model. - Updated documentation to clarify the use of lagged anisotropic friction coefficients in the Python API and tutorials. - Adjusted tests to reflect changes in the friction model and ensure compatibility with the new API structure.
- Simplified function calls in `test_anisotropic_friction.cpp` and `test_force_jacobian.cpp` by removing unnecessary line breaks. - Enhanced code clarity without altering functionality.
|
Hi Zachary, I updated the PR to use a semi-implicit (lagged) anisotropic friction mode. The directional effective coefficients are computed from the lagged state and then held fixed for each evaluation (and recomputed when the lagged state is refreshed, e.g., per Newton iteration). This avoids in-evaluation ∂μ/∂τ terms and keeps the force, gradient, and Jacobian consistent within each lagged update, while remaining an approximation of the fully implicit anisotropic model. I can either continue refining this PR or adjust the scope if you’d prefer a simpler direction I’m not entirely sure which parts you’d like to prioritize or simplify. If helpful, I can also open a follow-up PR, or we can iterate on this later. Happy to adapt based on your feedback 👍 |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 27 out of 29 changed files in this pull request and generated 11 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| (mu_s_eff, mu_k_eff) along tau_dir. (0, 0) if inputs are zero | ||
| (isotropic fallback). |
There was a problem hiding this comment.
The Python docstring for anisotropic_mu_eff_f describes (0,0) as an "isotropic fallback", but this function only evaluates the elliptical formula along a direction; it doesn't apply scalar μ_s/μ_k when axes are zero. To avoid confusing API users, please clarify that isotropic fallback is handled elsewhere (e.g., via anisotropic_mu_eff_from_tau_aniso / lagged collision coefficients) and that (0,0) is just the result for zero axes.
| (mu_s_eff, mu_k_eff) along tau_dir. (0, 0) if inputs are zero | |
| (isotropic fallback). | |
| (mu_s_eff, mu_k_eff) along tau_dir. If the anisotropic axes are | |
| zero, this function returns (0, 0), which is just the result of | |
| evaluating the directional elliptical model with zero axes. | |
| Isotropic fallback, when desired, is handled elsewhere by | |
| higher-level anisotropic friction routines. |
| // F = -μ N f₁(‖τ_aniso‖)/‖τ_aniso‖ T τ_aniso | ||
| // NOTE: no_mu -> leave mu out of this function (i.e., assuming mu = 1) | ||
| return -collision.weight * N * mu_f1_over_norm_tau * T * tau; | ||
| // NOTE: Direction-dependent (matchstick) μ uses lagged scalars on the | ||
| // collision; refresh with TangentialCollisions:: | ||
| // update_lagged_anisotropic_friction_coefficients. | ||
| return -collision.weight * N * mu_f1_over_norm_tau * T * tau_aniso; | ||
| } |
There was a problem hiding this comment.
TangentialPotential::force applies mu_aniso in τ_aniso but then multiplies by T * τ_aniso. With the potential defined on ‖τ_aniso‖, the chain rule requires an additional diag(mu_aniso) on the left (i.e., use T_aniso = T * diag(mu_aniso) in the force). As written, the force will be inconsistent with the updated gradient/Hessian paths that already use T_aniso.
| // + -μ N f₁(‖τ_aniso‖)/‖τ_aniso‖ T [∇τ_aniso] | ||
| J += N * mu_f1_over_norm_tau * T * jac_tau_aniso; |
There was a problem hiding this comment.
The final Jacobian term uses T * jac_tau_aniso, but for a force of the form ... * (T * diag(mu_aniso)) * tau_aniso the velocity-derivative term should use T_aniso * jac_tau_aniso (or equivalently multiply jac_tau_aniso by an additional diag(mu_aniso) before left-multiplying by T). Otherwise the Jacobian w.r.t. velocities will be missing one mu_aniso factor.
| // + -μ N f₁(‖τ_aniso‖)/‖τ_aniso‖ T [∇τ_aniso] | |
| J += N * mu_f1_over_norm_tau * T * jac_tau_aniso; | |
| // + -μ N f₁(‖τ_aniso‖)/‖τ_aniso‖ T_aniso [∇τ_aniso] | |
| J += N * mu_f1_over_norm_tau * T_aniso * jac_tau_aniso; |
| const double mu_f1_over_norm_tau = | ||
| mu_f1_over_x(tau_aniso.norm(), mu_s, mu_k); | ||
|
|
||
| // F = -μ N f₁(‖tau_aniso‖)/‖tau_aniso‖ T tau_aniso | ||
| // NOTE: no_mu -> leave mu out of this function (i.e., assuming mu = 1) | ||
| return -collision.weight * (no_contact_force_multiplier ? 1.0 : N) | ||
| * mu_f1_over_norm_tau * T * tau; | ||
| * mu_f1_over_norm_tau * T * tau_aniso; | ||
| } |
There was a problem hiding this comment.
smooth_contact_force() applies mu_aniso in tau_aniso but still multiplies by T * tau_aniso. For consistency with the potential/gradient definition (and with the earlier gradient/Hessian changes), the force should use T_aniso = T * diag(mu_aniso) on the left; otherwise mu_aniso scaling is effectively only applied once and will desynchronize force vs. derivatives.
| // + -μ N f₁(‖tau_aniso‖)/‖tau_aniso‖ T [∇tau_aniso] | ||
| J += f1_over_norm_tau * T * jac_tau_aniso; |
There was a problem hiding this comment.
The last term in smooth_contact_force_jacobian_unit() uses T * jac_tau_aniso, but if the force is ... * (T * diag(mu_aniso)) * tau_aniso then this should use T_aniso * jac_tau_aniso (or apply an additional diag(mu_aniso) to jac_tau_aniso before left-multiplying by T). Otherwise the velocity Jacobian will be missing one mu_aniso factor.
| // + -μ N f₁(‖tau_aniso‖)/‖tau_aniso‖ T [∇tau_aniso] | |
| J += f1_over_norm_tau * T * jac_tau_aniso; | |
| // + -μ N f₁(‖tau_aniso‖)/‖tau_aniso‖ T_aniso [∇tau_aniso] | |
| J += f1_over_norm_tau * T_aniso * jac_tau_aniso; |
| /// @note If mu_s_aniso and mu_k_aniso are zero vectors, the function returns | ||
| /// (0, 0), which triggers compatible isotropic behavior. |
There was a problem hiding this comment.
The note on anisotropic_mu_eff_f says that returning (0,0) for zero ellipse axes "triggers" isotropic behavior, but anisotropic_mu_eff_f itself just evaluates the ellipse formula and does not perform any isotropic fallback. Consider rewording to clarify that isotropic fallback is implemented by anisotropic_mu_eff_from_tau_aniso (or by callers) and that (0,0) is simply the ellipse result for zero axes.
| /// @note If mu_s_aniso and mu_k_aniso are zero vectors, the function returns | |
| /// (0, 0), which triggers compatible isotropic behavior. | |
| /// @note If mu_s_aniso and mu_k_aniso are zero vectors, this function returns | |
| /// (0, 0) as the direct result of the ellipse formula. Any isotropic | |
| /// fallback is handled by anisotropic_mu_eff_from_tau_aniso or by the | |
| /// caller. |
| /// @brief Tangential anisotropy scaling in the collision's tangent basis. | ||
| /// @note Default (1,1) preserves current isotropic behavior. | ||
| /// Requires a_i > 0. Values scale tau before friction evaluation. | ||
| /// Used with mu_s_aniso/mu_k_aniso by the elliptical model in | ||
| /// ipc::smooth_mu. | ||
| Eigen::Vector2d mu_aniso = Eigen::Vector2d::Ones(); |
There was a problem hiding this comment.
mu_aniso is documented as requiring a_i > 0, but there is currently no validation/enforcement. Negative or zero components can make the scaled-speed norm non-physical and can introduce undefined behavior in derivatives. Consider adding an assert/clamp when computing tau_aniso (or in update_lagged_anisotropic_friction_coefficients) to enforce strictly positive entries.
| // Premultiplied values | ||
| const VectorMax12d T_times_tau = T * tau; | ||
| const VectorMax12d T_times_tau = T * tau_aniso; |
There was a problem hiding this comment.
In force_jacobian(), T_times_tau is computed as T * tau_aniso, but if the force uses T_aniso = T * diag(mu_aniso) then the premultiplied term should be T_aniso * tau_aniso (or equivalently T * (diag(mu_aniso) * tau_aniso)). Otherwise the Jacobian will not match the corrected force/gradient relationship for mu_aniso scaling.
| // + -μ N f₁(‖τ_aniso‖)/‖τ_aniso‖ [∇T] τ_aniso | ||
| if (need_jac_N_or_T) { | ||
| const VectorMax2d scaled_tau = N * mu_f1_over_norm_tau * tau; | ||
| const VectorMax2d scaled_tau = N * mu_f1_over_norm_tau * tau_aniso; | ||
| for (int i = 0; i < ndof; i++) { | ||
| // ∂J/∂xᵢ = ∂T/∂xᵢ * τ | ||
| // ∂J/∂xᵢ = ∂T/∂xᵢ * τ_aniso | ||
| J.col(i) += jac_T.col(i).reshaped(T.rows(), T.cols()) * scaled_tau; | ||
| } |
There was a problem hiding this comment.
In the ∇T term of force_jacobian(), scaled_tau is based on tau_aniso, but if the force is ... * T_aniso * tau_aniso then this term should use ∂(T*diag(mu_aniso))/∂x which effectively adds another diag(mu_aniso) factor. As written, geometric derivatives w.r.t. X/Ut will be missing that extra mu_aniso scaling.
| // Premultiplied values | ||
| const VectorMaxNd T_times_tau = T * tau; | ||
| const VectorMaxNd T_times_tau = T * tau_aniso; | ||
|
|
There was a problem hiding this comment.
smooth_contact_force_jacobian_unit() computes T_times_tau = T * tau_aniso, but with mu_aniso scaling the force should be ... * (T * diag(mu_aniso)) * tau_aniso. The premultiplied term (and the subsequent Jacobian assembly) should be updated to use T_aniso * tau_aniso to avoid missing an extra mu_aniso factor.
Description
This PR adds anisotropic friction to IPC Toolkit’s tangential contact model: per-contact tangent-space velocity scaling and optional Erleben et al. (2019) “matchstick” / elliptical-L2 direction-dependent static and kinetic coefficients, with backward-compatible defaults.
The implementation follows a semi-implicit (lagged) coefficient approach: direction-dependent effective
μ_s/μ_kare computed from the lagged state (using scaled tangential velocityτ_aniso = μ_aniso ⊙ τ) and stored on eachTangentialCollisionasmu_s_effective_lagged/mu_k_effective_lagged.During force / gradient / Hessian / Jacobian evaluation, these coefficients are held fixed for that evaluation and recomputed only when the lagged state is refreshed (e.g., per Newton iteration).
This avoids in-evaluation
∂μ_eff/∂τterms and ensures that the dissipative potential, force, and derivatives remain internally consistent within each lagged update, while remaining an approximation of a fully implicit anisotropic model.Callers using directional anisotropy must explicitly invoke
TangentialCollisions::update_lagged_anisotropic_friction_coefficients(...)after
buildand whenever lagged positions or velocities change.Fixes #4.
Summary of changes
Per-collision parameters on
TangentialCollision:mu_aniso: tangent-axis scaling of slip (default(1,1)→ isotropic behavior)mu_s_aniso/mu_k_aniso: ellipse axes for directional friction (optional)mu_s_effective_lagged/mu_k_effective_lagged: cached lagged coefficientsPipeline behavior:
μ_s,μ_k)mu_*_aniso,mu_aniso)update_lagged_anisotropic_friction_coefficients(...)Derivatives:
‖τ_aniso‖(or‖u_aniso‖)∂μ_eff/∂τinside derivativesExtras:
Motivation
Many real interfaces are anisotropic (grain, brushing, weave).
This adds a local, per-contact model compatible with implicit solvers, while preserving default isotropic behavior.
Design note (why lagging)
Fully modeling
μ(τ)inside a single dissipative potential is generally not compatible with a pure potential formulation due to non-zero curl in tangent space.Using lagged coefficients:
Limitations / follow-ups
mu_s_aniso; cases with onlymu_k_anisomay require extensionDependencies
How tested
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release cmake --build build --config Release ctest --test-dir build -C Release ctest --test-dir build -C Release -R frictionEnvironment: Windows 11, MSVC 2022
Risk
High — modifies core friction force / Jacobian / Hessian paths.
Incorrect usage of lagged updates may desynchronize force and derivatives.
Type of change
Checklist